package programme;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;







/**
 * Classe implémentant un programme. Les classes ProgDeterministe ou ProgStochastique héritent de celle-ci.
 * 
 *
 */
public abstract class Programme {

	/** Fonction objectif du programme*/
	protected FonctObjectif objectif;
	
	/** Liste des contraintes du programme*/
	protected ArrayList<Contrainte> contraintes;
		
	/** dictionnaire de l'ensemble des vecteur de variables du programme*/
	protected HashMap<String, VecteurVariables> variables; 
	
	public Programme(){
		contraintes = new ArrayList<Contrainte>();
		variables = new HashMap<String, VecteurVariables>();
	}
	
	
	public FonctObjectif getObjectif() {
		return objectif;
	}

	public void setObjectif(FonctObjectif fct) {
		this.objectif = fct;
	}


	public ArrayList<Contrainte> getContraintes() {
		return contraintes;
	}

	public void setContraintes(ArrayList<Contrainte> contraintes) {
		this.contraintes = contraintes;
	}


	public HashMap<String, VecteurVariables> getVecteursVariables() {
		return variables;
	}

	public void setVariables(HashMap<String, VecteurVariables> variables) {
		this.variables = variables;
	}
	
	public ArrayList<Variable> getVariables(){
		ArrayList<Variable> vars = new ArrayList<Variable>();
		for (VecteurVariables vec : variables.values()) {
			vars.addAll(vec.getVariables());
		}
		return vars;
	}
	
	public void addContrainte(Contrainte c){
		contraintes.add(c);
	}

	public void addVecteurVariables(VecteurVariables vecteur){
		variables.put(vecteur.getNom(), vecteur);
	}
	
	/**
	 * Vérifie qu'une solution donnée satisfait les contraintes du problème
	 * @param 	solution La solution à tester
	 * @return 	true si la solution vérifie les contraintes, false sinon
	 */
	public boolean satisfaitContraintes(Solution solution){
		
		boolean satisfait = true;
		
		for (Contrainte c : contraintes){
			
			satisfait &= c.estSatisfaitePar(solution);
			
		}
		
		for (Variable v : solution.getValeurs().keySet()){
			
			satisfait &= solution.getValeurs().get(v) <= v.getBorneMax();
			satisfait &= solution.getValeurs().get(v) >= v.getBorneMin();
			
//			if (solution.getValeurs().get(v) > v.getBorneMax())
//				System.out.println(v + " : borne max dépassée");
//			
//			if (solution.getValeurs().get(v) < v.getBorneMin())
//				System.out.println(v + " : borne min dépassée");
			
		}
		
		return satisfait;
	}
	
	
	/**
	 * Fonction donnant un numero absolue aux variables. Necessaire pour lpsolve
	 */
	public void nommerVariables(){
		int i = 0;
		for (Iterator<Entry<String, VecteurVariables>> iterator = variables.entrySet().iterator(); iterator.hasNext();) {
			Entry<String, VecteurVariables> entry = iterator.next();
			VecteurVariables v = entry.getValue();
			for(Variable var:v.getVariables()){
				var.setNumAbsolue(i);
				i++;
			}
		}
	}
	
	/**
	 * Fonction retournant la variable ayant pour numero absolue numAbsolue
	 * @param numAbsolue
	 * @return 
	 */
	public Variable getVar(int numAbsolue){
		int i=0;
		if (numAbsolue < this.getNbVars()){
			for (Iterator<Entry<String, VecteurVariables>> iterator = variables.entrySet().iterator(); iterator.hasNext();) {
				Entry<String, VecteurVariables> entry = iterator.next();
				VecteurVariables v = entry.getValue();
				if(v.getVariables().size()+i > numAbsolue){
					for(Variable var:v.getVariables()){
						if (var.getNumAbsolue() == numAbsolue){
							return var;
						}
					}				
				} else {
					i += v.getVariables().size();
				}
			}
		}
		return null;
	}
	
	public abstract Programme progSansContrainteRelacher();
	
	public abstract Programme lineariserFonctObj();

	public int getNbVars(){
		int nb = 0;
		for (VecteurVariables vec : variables.values()) {
			nb += vec.getVariables().size();
		}
		return nb;
	}
	
	public String toString(){
		StringBuffer buf = new StringBuffer();
		if( objectif.isMin() ){
			buf.append("min ");
		}else{
			buf.append("max ");
		}
		buf.append(objectif.getFonction() + "\n");
		for (Contrainte c : contraintes) {
			buf.append(" | ");
			buf.append( c);
			buf.append("\n");
		}
		for (Variable var : getVariables()) {
			buf.append(var);
			buf.append(" [");
			buf.append(var.getBorneMin());
			buf.append("..");
			buf.append(var.getBorneMax());
			buf.append("] type : ");
			buf.append(var.getType());
			buf.append("\n");
		}
		
		return buf.toString();
	}
}
